package com.douyuehan.doubao.wechatpay.service.serviceimp;

import com.douyuehan.doubao.common.exception.ArgumentException;
import com.douyuehan.doubao.wechatpay.jparepository.CustomerRepository;
import com.douyuehan.doubao.wechatpay.jparepository.ParkingPaymentRepository;
import com.douyuehan.doubao.wechatpay.jparepository.ProjectRepository;
import com.douyuehan.doubao.wechatpay.model.dto.UnifiedorderParam;
import com.douyuehan.doubao.wechatpay.model.dto.WechatParam;
import com.douyuehan.doubao.wechatpay.model.entity.Customers;
import com.douyuehan.doubao.wechatpay.model.entity.ParkingPayment;
import com.douyuehan.doubao.wechatpay.model.entity.Project;
import com.douyuehan.doubao.wechatpay.model.vo.*;
import com.douyuehan.doubao.wechatpay.service.WechatService;
import com.douyuehan.doubao.wechatpay.utils.*;
import com.google.common.base.Strings;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.XmlFriendlyReplacer;
import com.thoughtworks.xstream.io.xml.XppDriver;
import okhttp3.MediaType;
import okhttp3.Response;
import org.apache.commons.codec.digest.DigestUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.lang.invoke.MethodHandles;
import java.math.BigDecimal;
import java.net.URLDecoder;
import java.sql.Timestamp;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

@Service
public class WechatServiceImpl implements WechatService {

    private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());

    @Resource
    private ProjectRepository projectRepository;

    @Resource
    private CustomerRepository customerRepository;

    @Resource
    ParkingPaymentRepository paymentRepository;

    @Override
    public String getSPappid() {
        WechatAccessTokenHolder tokenHolder = WechatAccessTokenHolder.getInstance();
        String token = "";
        if (tokenHolder.getAccessToken() != null) {
            token = tokenHolder.getAccessToken().getAccessToken();
        }
        if (Strings.isNullOrEmpty(token)) {
            throw new ArgumentException("获取微信Token失败", 500);
        }
        return "";
    }

    @Override
    public WechatJSConfigTo getJSConfig(String url) {
        if (Strings.isNullOrEmpty(url)) throw new ArgumentException("缺少必要参数", 500);

        url = url.replaceAll("-", "%");

        String urlString;
        try {
            urlString = URLDecoder.decode(url, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            throw new ArgumentException("地址转换出错", 500);
        }

        WechatAccessTokenHolder tokenHolder = WechatAccessTokenHolder.getInstance();
        String token = "";
        if (tokenHolder.getAccessToken() != null) {
            token = tokenHolder.getAccessToken().getAccessToken();
        }
        if (Strings.isNullOrEmpty(token)) {
            throw new ArgumentException("获取微信Token失败", 500);
        }


        WechatJSConfigTo to = new WechatJSConfigTo();
        to.setAppId(Constants.APPID);
        to.setNonceStr(GeneratorUtil.getRandomStringByLength(16));
        to.setTimestamp(String.valueOf(System.currentTimeMillis() / 1000));
        to.setJsapiTicket(WechatJsapiTicketHolder.getInstance().getJsapiTicket().getTicket());
        String value = "jsapi_ticket=" + WechatJsapiTicketHolder.getInstance().getJsapiTicket().getTicket() + "&noncestr=" + to.getNonceStr() + "&timestamp=" + to.getTimestamp() + "&url=" + urlString;
        log.debug(value);
        to.setSignature(DigestUtils.shaHex(value).toLowerCase());

        return to;
    }

    @Override
    public MessageTo<WechatUserTo> getOpenId(String code) {

        if (Strings.isNullOrEmpty(code)) throw new ArithmeticException("必要参数不能为空");

        String appId = Constants.APPID;
        String appSecret = Constants.APP_SECRECT;

        String params = "appid=" + appId + "&secret=" + appSecret + "&code=" + code + "&grant_type=authorization_code";
        // sending request
        String sr = HttpRequest.sendGet("https://api.weixin.qq.com/sns/oauth2/access_token", params);

        // analysis request content
        net.sf.json.JSONObject json = net.sf.json.JSONObject.fromObject(sr);

        // getting open_id
        String openId = json.get("openid").toString();

        WechatUserTo to = new WechatUserTo();
        to.openid = openId;

        MessageTo<WechatUserTo> msg = new MessageTo<>();

        msg.setData(to);
        return msg;
    }


    @Override
    public MessageTo<WxPaymentTo> wxPay(WechatParam param, HttpServletRequest request) {
        if (Strings.isNullOrEmpty(param.getOpenId()) || param.getCustomerId() == 0 || param.getProjectId() == 0)
            throw new ArgumentException("缺少支付参数", 500);

        Project project = projectRepository.findById(param.getProjectId());
        if (project == null) throw new ArgumentException("未找到具体项目信息");

        String nonceStr = GeneratorUtil.getRandomStringByLength(32);
        String outTradeNo = DateUtil.dateToStr(new Date(), "yyyyMMddHHmmss") + GeneratorUtil.getRandomStringByLength(10);
        String serverIp = WxpayUtil.getIpAddr(request);
        BigDecimal bigDecimal = new BigDecimal(project.getMaxOrderNumber());
        BigDecimal money = project.getOrderMoney().multiply(bigDecimal);
        String body = "测试开盘支付";

        ParkingPayment parkingPayment = new ParkingPayment();
        parkingPayment.setCreateTime(DateUtil.getCurrentTimestamp());
        parkingPayment.setCreateBy(param.getCustomerId());
        parkingPayment.setProjectId(param.getProjectId());
        parkingPayment.setOutTradeNo(outTradeNo);
        parkingPayment.setPayMoney(money);
        parkingPayment.setBody(body);
        parkingPayment.setSiteNumber(param.getSiteNumber());
        parkingPayment.setCustomerId(param.getCustomerId());
        paymentRepository.saveAndFlush(parkingPayment);

        //        BigDecimal multiply = new BigDecimal("100");
        //        money = money.multiply(multiply).setScale(0, BigDecimal.ROUND_DOWN);

        UnifiedorderParam unifiedorderParam = new UnifiedorderParam();
        unifiedorderParam.setAppid(Constants.APPID);
        unifiedorderParam.setMch_id(Constants.MCH_ID);
        unifiedorderParam.setNonce_str(nonceStr);
        unifiedorderParam.setBody(body);
        unifiedorderParam.setOut_trade_no(outTradeNo);
        unifiedorderParam.setTotal_fee("1");
        //        unifiedorderParam.setTotal_fee("1");
        unifiedorderParam.setSpbill_create_ip(serverIp);
        unifiedorderParam.setNotify_url(Constants.WECHAT_NOTIFY_URL);
        unifiedorderParam.setTrade_type("JSAPI");
        unifiedorderParam.setOpenid(param.getOpenId());

        Map<String, String> packageParams = new HashMap<>();
        packageParams.put("appid", Constants.APPID);
        packageParams.put("mch_id", Constants.MCH_ID);
        packageParams.put("nonce_str", nonceStr);
        packageParams.put("body", body);
        packageParams.put("out_trade_no", outTradeNo);
        packageParams.put("total_fee", "1");
        packageParams.put("spbill_create_ip", serverIp);
        packageParams.put("notify_url", Constants.WECHAT_NOTIFY_URL);
        packageParams.put("trade_type", "JSAPI");
        packageParams.put("openid", param.getOpenId());

        String sign = WxpayUtil.generateSignature(packageParams, Constants.MCH_SERECT);
        // 通过签名算法计算得出的签名值
        unifiedorderParam.setSign(sign);

        XStream xParamStream = new XStream(new XppDriver(new
                XmlFriendlyReplacer("_-", "_")));
        xParamStream.alias("xml", UnifiedorderParam.class);

        okhttp3.RequestBody requestBody = okhttp3.RequestBody.create(MediaType.parse("text/xml;charset=utf-8"), xParamStream.toXML(unifiedorderParam));

        Response response = null;
        try {
            response = WxpayUtil.post(Constants.UNIFIEDORDER_URL, requestBody);
        } catch (IOException e) {
            e.printStackTrace();
        }
        String resultXml = null;
        try {
            resultXml = response.body().string();
            System.out.println(resultXml);
        } catch (IOException e) {
            e.printStackTrace();
        }

        WxPaymentTo result = new WxPaymentTo();
        Map<String, String> packageParams1 = new HashMap<>();

        XStream xStream = new XStream();
        xStream.alias("xml", WxPaymentReturnTo.class);
        WxPaymentReturnTo returnTo = (WxPaymentReturnTo) xStream.fromXML(resultXml);
        System.out.println(returnTo);

        String timeStamp = String.valueOf(System.currentTimeMillis() / 1000);

        if (returnTo.getReturn_code().equals("SUCCESS")) {

            packageParams1.put("appId", Constants.APPID);
            packageParams1.put("package", "prepay_id=" + returnTo.getPrepay_id());
            packageParams1.put("timeStamp", timeStamp);
            packageParams1.put("signType", "MD5");
            packageParams1.put("nonceStr", nonceStr);
        }
        String paySign = WxpayUtil.generateSignature(packageParams1, Constants.MCH_SERECT);
        result.setPaySign(paySign);
        result.setNonceStr(nonceStr);
        result.setTimeStamp(timeStamp);
        result.setAppId(Constants.APPID);
        result.setPackageStr("prepay_id=" + returnTo.getPrepay_id());

        MessageTo<WxPaymentTo> msg = new MessageTo<>();
        msg.setData(result);
        return msg;
    }

    @Override
    @Transactional
    public String wechatNotify(HttpServletRequest request, HttpServletResponse response) throws IOException, ParseException {

        // 预先设定返回的 response 类型为 xml
        response.setHeader("Content-type", "application/xml");
        // 读取参数，解析Xml为map
        Map<String, String> map = WxpayUtil.transferXmlToMap(WxpayUtil.readRequest(request));
        // 转换为有序 map，判断签名是否正确
        boolean isSignSuccess = false;

        try {
            isSignSuccess = WxpayUtil.checkSign(new TreeMap<>(map), Constants.MCH_SERECT);
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (isSignSuccess) {
            // 签名校验成功，说明是微信服务器发出的数据
            String outTradeNo = map.get("out_trade_no");
            // 可在此持久化微信传回的该 map 数据
            //..
            if (map.get("return_code").equals("SUCCESS")) {

                ParkingPayment parkingPayment = paymentRepository.findByOutTradeNo(outTradeNo);
                if (parkingPayment != null) {
                    parkingPayment.setPayResult(map.get("result_code"));
                    parkingPayment.setOpenid(map.get("openid"));
                    parkingPayment.setTransactionId(map.get("transaction_id"));
                    Date date = new SimpleDateFormat("yyyyMMddHHmmss").parse(map.get("time_end"));//先按照原格式转换为时间
                    Timestamp createTime = new Timestamp(date.getTime());
                    parkingPayment.setPayTime(createTime);

                    paymentRepository.saveAndFlush(parkingPayment);

                    Optional<Customers> optional = customerRepository.findById(parkingPayment.getCustomerId());
                    Customers customers = optional.orElseThrow(() -> new ArgumentException("未找到该客户", 500));
                    customers.setPayTime(parkingPayment.getPayTime());
                    customers.setPayNumber(parkingPayment.getSiteNumber() + "");
                    customers.setPayMoney(parkingPayment.getPayMoney());
                    customers.setPayOutTradeNo(parkingPayment.getOutTradeNo());
                    customerRepository.saveAndFlush(customers);
                }
            }
            log.debug("-------------------返回----------------------");
            return "<xml>\n" +
                    "  <return_code><![CDATA[SUCCESS]]></return_code>\n" +
                    "  <return_msg><![CDATA[OK]]></return_msg>\n" +
                    "</xml>";
        } else {
            // 签名校验失败（可能不是微信服务器发出的数据）
            return "<xml>\n" +
                    "  <return_code><![CDATA[FAIL]]></return_code>\n" +
                    "  <return_msg><![CDATA[]]></return_msg>\n" +
                    "</xml>";
        }
    }
}
